package com.jfinal.plugin.activerecord;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.jfinal.kit.StrKit;

public class SqlPro {
	private final Config config;
	private String s;//方言字符串

	private String alias;// 别名
	private String sql;
	private Object[] paras;

	private String whereSql;

	public SqlPro() {
		if (DbKit.config == null)
			throw new RuntimeException("The main config is null, initialize ActiveRecordPlugin first");
		this.config = DbKit.config;
	}

	public SqlPro(String configName) {
		this.config = DbKit.getConfig(configName);
		if (this.config == null)
			throw new IllegalArgumentException("Config not found by configName: " + configName);
	}

	public String getSql() {
		return sql;
	}

	public Object[] getParas() {
		return paras;
	}

	public SqlPro setAlias(String alias) {
		this.alias = alias;
		return this;
	}

	public SqlPro where(String namedWhereSql, Map<String, Object> paraMap) {
		ParsedSql ps = ParsedSql.parseNamedSqlStatement(namedWhereSql, paraMap);
		Object[] paras = ps.getParas();
		String sql = ps.getSql();
		return where(sql, paras);
	}

	public SqlPro where(String whereSql, Object... paras) {
		this.whereSql = whereSql;
		this.paras = paras;
		return this;
	}

	/*-----------------------------  buildUpdateSql begin ------------------------------*/
	public <M extends Model<?>> SqlPro buildUpdateSql(Model<M> model) {
		String tableName = TableMapping.me().getTable(model.getClass()).getName();
		Map<String, Object> attrs = model.getAttrs();
		return buildUpdateSql(tableName, attrs);
	}

	public SqlPro buildUpdateSql(String tableName, Record record) {
		return buildUpdateSql(tableName, record.getColumns());
	}

	public SqlPro buildUpdateSql(String tableName, Map<String, Object> attrs) {
		List<Object> parasList = new ArrayList<Object>();
		StringBuilder sql = new StringBuilder();
		String alias = getAlias(tableName);
		sql.append("update ").append(wrap(tableName.trim())).append(" " + alias).append(" set ");
		boolean bl = true;
		for (Entry<String, Object> e : attrs.entrySet()) {
			String colName = e.getKey();
			if (parasList.size() > 0 && bl) {
				sql.append(", ");
				bl = false;
			}
			sql.append(alias + "." + wrap(colName)).append(" = ? ");
			parasList.add(e.getValue());
		}
		parasList.addAll(Arrays.asList(this.paras));
		addWhere(sql);
		this.sql = sql.toString();
		this.paras = parasList.toArray();
		return this;
	}

	/*-----------------------------  buildUpdateSql end ------------------------------*/

	/*---------------------------- buildFindSql begin ----------------------------*/
	public SqlPro buildFindSql(String tableName) {
		return buildFindSql(tableName, "*");
	}

	public SqlPro buildFindSql(String tableName, String columns) {
		StringBuilder sql = new StringBuilder("select ");
		String alias = getAlias(tableName);
		if (columns.trim().equals("*")) {
			sql.append(alias + "." + columns);
		} else {
			String[] columnsArray = columns.split(",");
			for (int i = 0; i < columnsArray.length; i++) {
				if (i > 0)
					sql.append(", ");
				sql.append(alias + "." + wrap(columnsArray[i].trim()));
			}
		}
		sql.append(" from ");
		sql.append(wrap(tableName.trim()));
		sql.append(" " + alias + " ");
		addWhere(sql);
		this.sql = sql.toString();
		return this;
	}

	/*---------------------------- buildFindSql end ----------------------------*/

	/*-----------------------------  buildDeleteSql begin ------------------------------*/
	public SqlPro buildDeleteSql(String tableName) {
		StringBuilder sql = new StringBuilder();
		String alias = getAlias(tableName);
		sql.append("delete from ").append(wrap(tableName.trim())).append(" " + alias);
		addWhere(sql);
		this.sql = sql.toString();
		return this;
	}

	/*-----------------------------  buildDeleteSql end ------------------------------*/

	/*-----------------------------  buildSaveSql begin ------------------------------*/
	public SqlPro buildSaveSql(String tableName, Map<String, Object> attrs) {
		List<Object> parasList = new ArrayList<Object>();
		StringBuilder sql = new StringBuilder();
		String alias = getAlias(tableName);
		sql.append("insert into ");
		sql.append(tableName.trim()).append(" " + alias).append("(");
		StringBuilder temp = new StringBuilder();
		temp.append(") values(");

		for (Entry<String, Object> e : attrs.entrySet()) {
			if (parasList.size() > 0) {
				sql.append(", ");
				temp.append(", ");
			}
			sql.append(alias + "." + e.getKey());
			temp.append("?");
			parasList.add(e.getValue());
		}
		sql.append(temp.toString()).append(")");
		addWhere(sql);
		parasList.addAll(Arrays.asList(this.paras));
		this.sql = sql.toString();
		this.paras = parasList.toArray();
		return this;
	}

	/*-----------------------------  buildSaveSql end ------------------------------*/

	protected void addWhere(StringBuilder sql) {
		if (StrKit.notBlank(whereSql)) {
			sql.append(" where 1 = 1 ");
			sql.append(" and ");
			sql.append(whereSql);
		}
	}

	// 包裹 
	protected String wrap(String name) {
		if (s == null) {
			String dialectName = config.getDialect().getClass().getSimpleName();
			//		String ansi = "AnsiSqlDialect";
			//		String oracle = "OracleDialect";
			//		String sqlite3 = "Sqlite3Dialect";
			String mysql = "MysqlDialect";
			String postgre = "PostgreSqlDialect";
			if (mysql.equalsIgnoreCase(dialectName)) {
				s = "`";
			} else if (postgre.equalsIgnoreCase(dialectName)) {
				s = "\"";
			} else {// ansi,oracle,sqlite3
				s = "";
			}
		}
		if (s.isEmpty())
			return name;
		return s + name + s;
	}

	protected String getAlias(String tableName) {
		if (alias == null)
			alias = tableName;
		return alias;
	}
}
